#pragma once

#include <iostream>
#include <sys/select.h>
#include <vector>
#include "Socket.hpp"
#include "Inet_Addr.hpp"

using namespace mySocket;

class selectServer
{
    const static int N = sizeof(fd_set) * 8;
    const static int defaultFd = -1;

private:
    std::string fdsString()
    {
        std::string ret;
        for (auto fd : fds)
        {
            if (fd != defaultFd)
                ret += std::to_string(fd) + " ";
        }
        return ret;
    }
    void addFd()
    {
        InetAddr clientAddr;
        int sockfd = _listenSock->socketAccept(&clientAddr)->getSockd(); // 资源就绪，无需等待，直接accept

        // 需要将sockfd放入fds中
        // 找一个空位
        int pos = 0;
        for (; pos < N; pos++)
            if (fds[pos] == defaultFd)
                break;
        if (pos == N)
        {
            // fds满了，无法再等待多余的fd
            ::close(sockfd);
            LOG(DEBUG, "add erro, the fds is full");
        }
        else
        {
            fds[pos] = sockfd;
            LOG(DEBUG, "add fd: %d", sockfd);
            LOG(DEBUG, "fds string: %s", fdsString().c_str());
        }
    }

    void serviceIO(int& fd)
    {
        char buffer[1024] = {0};
        int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n == 0)
        {
            // 读到文件尾，客户端断开连接
            // 此时要关闭fd，并且还要将这个fd从fds中移除(重新置为默认)
            std::cout << "client exited" << std::endl;
            ::close(fd);
            fd = defaultFd;
            LOG(DEBUG, "the fds: %s", fdsString().c_str());
        }
        else if (n < 0)
        {
            std::cout << "recv error" << std::endl;
            ::close(fd);
            fd = defaultFd;
            LOG(DEBUG, "the fds: %s", fdsString().c_str());
        }
        else
        {
            // Echo
            LOG(DEBUG, "recv: %s", buffer);
            std::string massage = "Echo# " + std::string(buffer);
            send(fd, massage.c_str(), massage.size(), 0);
        }
    }

    void selectHandler(fd_set &rfd)
    {
        // 判断是哪些fd已经就绪
        for (auto &fd : fds)
        {
            if (FD_ISSET(fd, &rfd))
            {
                if (fd == defaultFd)
                    continue;

                if (fd == _listenSock->getSockd())
                {
                    //r如果是listenfd准备就绪，就说明有新的连接
                    addFd();
                }
                else
                {
                    // IO套接字已经就绪，即客户端已经发了消息过来，可以直接读
                    serviceIO(fd);
                }
            }
        }
    }

public:
    selectServer(uint16_t port)
    {
        _listenSock = new tcpSocket;
        _listenSock->tcpServerInit(InetAddr(port));

        // 初始化fd数组，并将listenfd放入
        fds = std::vector<int>(N, -1);
        fds[0] = _listenSock->getSockd();
    }

    void loop()
    {
        while (true)
        {

            fd_set rfd;
            FD_ZERO(&rfd);

            // 每一次select前，都需要将需要等待的fd放入rfd中
            // 并记录最大值
            int maxFd = defaultFd;
            for (auto fd : fds)
            {
                if (fd != defaultFd)
                    FD_SET(fd, &rfd);
                maxFd = maxFd >= fd ? maxFd : fd;
            }

            timeval timeout = {1, 0};
            int n = select(maxFd + 1, &rfd, nullptr, nullptr, &timeout);
            if (n == 0)
                std::cout << "资源未就绪, time out" << std::endl;
            else if (n < 0)
            {
                std::cout << "select error" << std::endl;
                break;
            }
            else
            {
                std::cout << "资源就绪 " << n << std::endl;
                
                selectHandler(rfd);
            }
        }
    }

private:
    socket_ptr _listenSock;
    std::vector<int> fds; // 辅助数组，存放所有的fd
};